package binding

import (
	"strings"
	"sync"
)

// 验证方法
// val=值，arg=参数
type Handler func(val interface{}, arg string) bool

// 注册的自定义验证器
var validaterRegisters = make(map[string]Handler)

// 注册锁
var registerLock = new(sync.Mutex)

// 添加验证器
func RegValidater(name string, fn Handler) {
	registerLock.Lock()
	defer registerLock.Unlock()
	validaterRegisters[name] = fn
}

// 验证器
type validater struct {
	mustRequired   bool   //是否有必要性验证
	canEmpty       bool   //是否可以为空
	requiredNotice string //必要性验证提示
	ruleList       []rule //规则列表
}

// 规则参数
type rule struct {
	name string //验证函数
	msg  string //错误消息
	arg  string //参数
}

// 创建验证对象，如果规则不匹配则不执行规则
// ruleTag = 验证规则参数
func newValidate(ruleTag string) *validater {
	obj := new(validater)
	obj.ruleList = make([]rule, 0)
	roleArray := strings.Fields(ruleTag)
	for _, item := range roleArray {
		arr := strings.Split(item, "->")
		var pattens, msg string
		if l := len(arr); l == 1 {
			pattens, msg = arr[0], "验证失败"
		} else if l == 2 {
			pattens, msg = arr[0], arr[1]
		} else {
			continue
		}
		for _, r := range strings.Split(pattens, ",") {
			ru := rule{msg: msg}
			ru.name, ru.arg = obj.arg(r)
			switch ru.name {
			case "omitempty":
				obj.canEmpty = true
				continue
			case "required":
				obj.mustRequired = true
				obj.requiredNotice = msg
			}
			obj.ruleList = append(obj.ruleList, ru)
		}
	}
	return obj
}

// 是否为空
// 如果允许为空且val为空，则处理下一个
// 如果不允许为空且val为空，则返回错误
func (this *validater) isRequiredAndNil(val interface{}) (msg string, goon bool) {
	//无规则则不用执行
	//if len(this.ruleList) == 0 {
	//return "", true
	//}
	if !IsNotNull(val) {
		if this.mustRequired {
			return this.requiredNotice, false
		}
		return "", false
	}
	return "", true
}

// 验证,如果允许为空，且当前数据为空则不验证
func (this *validater) check(val interface{}) (msg string, ok bool) {
	if this.canEmpty && !IsNotNull(val) {
		return "", true
	}
	for _, rule := range this.ruleList {
		if !this.handler(rule, val) {
			return rule.msg, false
		}
	}
	return "", true
}

// 验证处理器,先执行系统验证，系统验证无的时候再执行自定义验证
func (this *validater) handler(r rule, val interface{}) bool {
	switch r.name {
	case "required":
		return IsNotNull(val) //必须
	case "int":
		return IsInt(val)
	case "float":
		return IsFloat(val)
	case "eq":
		return IsEq(val, r.arg) //等于
	case "in":
		return IsIn(val, r.arg) //包含
	case "not-in":
		return IsNotIn(val, r.arg) //不包含
	case "gt":
		return IsGreatThan(val, ParseFloat(r.arg)) //大于
	case "gte":
		return IsGreatThanAndEqual(val, ParseFloat(r.arg)) //大于等于
	case "lt":
		return IsLessThan(val, ParseFloat(r.arg)) //小于
	case "lte":
		return IsLessThanAndEuqal(val, ParseFloat(r.arg)) //小于等于
	case "len":
		return IsLen(val, int(ParseInt(r.arg))) //长度等于
	case "min":
		return IsMin(val, int(ParseInt(r.arg))) //长度最小
	case "max":
		return IsMax(val, int(ParseInt(r.arg))) //长度最大
	case "match":
		return IsMatch(val, r.arg) //正则式匹配
	case "mobile":
		return IsMobile(val) //手机号码
	case "tel":
		return IsTel(val) //座机
	case "qq":
		return IsQQ(val) //QQ号
	case "postcode":
		return IsPostcode(val) //邮编
	case "account":
		return IsAccount(val) //账号
	case "password":
		return IsPassword(val) //密码
	case "password2":
		return IsPassword2(val) //中度密码
	case "password3":
		return IsPassword3(val) //重度密码
	case "email":
		return IsEmail(val) //邮箱
	case "url":
		return IsUrl(val) //url地址
	case "domain":
		return IsDomain(val) //域名
	case "mac":
		return IsMac(val) //MAC地址
	case "ip":
		return IsIP(val) //ip地址
	case "ip4":
		return IsIP4(val) //ip4地址
	case "ip6":
		return IsIP6(val) //ip6地址
	case "id-number":
		return IsIDNumber(val) //身份证号码
	case "date":
		return IsDate(val) //日期
	case "datetime":
		return IsDatetime(val) //日期时间
	case "hour-minute": //时间（小时分钟）
		return IsHourMinute(val)
	case "luhn":
		return IsLuHn(val) //银行卡号
	case "no-repeat": //数组不重复
		return IsArrayRepeat(val)
	default: //默认调用注册方法
		registerLock.Lock()
		defer registerLock.Unlock()
		if fn, ok := validaterRegisters[r.name]; ok {
			return fn(val, r.arg)
		}
		return false
	}
}

// 获取参数
func (this *validater) arg(r string) (name string, arg string) {
	arr := strings.Split(r, "=")
	if len(arr) == 1 {
		name = arr[0]
	} else if len(arr) > 1 {
		name, arg = arr[0], arr[1]
	}
	return
}
